Patch


a Player


An Instr (Instrument) is a function that has a unique name attached to it.

A Patch specifies an Instr by name and specifies inputs that will be passed to the Instr function when it is actually played.  A Patch is the player that actually plays an Instr's sound function.


Patch(instr,args)


instr

an instr name

a function

an Instr object

args - an array of 

floats

players of any type

many other types of objects

It all depends what the instr function accepts.





(

// given a simple instrument

Instr(\Pulse,{ arg freq=440.0,width=0.5,mul=1.0;


Pulse.ar( freq, width, mul )

});


// a patch specifies inputs (arguments) to that instrument

p = Patch( \Pulse, [

300,

0.2,

0.1

]);


//the default server will be booted, the SynthDef written and loaded

p.play;


)


p.stop;


// command-. will also stop all sounds



Instr can be specified as 


by the Instr name (see Instr)

// the Instr stores itself when created

(

// this can be kept in a separate file

Instr(\bubbles, { arg amp=1.0;

var f, zout;

f = LFSaw.kr(0.4, 0, 24, LFSaw.kr([8,7.23], 0, 3, 80)).midicps;

zout = CombN.ar(SinOsc.ar(f, 0, 0.04), 0.2, 0.2, 4);

zout * amp

});

// the patch retrieves it

p = Patch(\bubbles,[0.4] );

p.gui

)

a Function

(

p = Patch({ arg freq=100,amp=1.0;

SinOsc.ar([freq,freq + 30],0,amp)

},[

500,

0.3

]);

p.gui

)

or directly as an Instr object

(

i = Instr("help-Patch",{ arg freq=100,amp=1.0;

SinOsc.ar([freq,freq + 30],0,amp)

});

p = Patch(i,[ 500, 0.3 ]);

p.gui

)



The args


The args may be whatever the instr function is willing to accept.

These may be simple floats,

Envelopes,

Audio rate Players

Control rate Players



(

Instr(\bubbles, { arg amp=1.0;

var f, zout;

f = LFSaw.kr(0.4, 0, 24, LFSaw.kr([8,7.23], 0, 3, 80)).midicps;

zout = CombN.ar(SinOsc.ar(f, 0, 0.04), 0.2, 0.2, 4);

zout * amp

});


Instr(\rlpf,{ arg audio=0,freq=500,rq=0.1;

RLPF.ar(audio, freq, rq)

});


p = Patch(\rlpf,[

q = Patch(\bubbles)

]);


p.gui


)


Each argument has a Spec.  See Instr for how these specs are determined.


An argument may be nil.  For a nil argument, Patch will ask the spec to create a suitable defaultControl.


ControlSpec :  KrNumberEditor

StaticSpec    :  NumberEditor  (for quantities or max delay times etc.)

a static spec is a non-modulateable control

EnvSpec       : EnvEditor

SampleSpec : Sample



The instrument may specify default values in the arg names of its function:

(

Instr(\bubbles, { arg speed = 0.4, amp=0.4;

var f, zout;

f = LFSaw.kr(speed, 0, 24, LFSaw.kr([8,7.23], 0, 3, 80)).midicps;

zout = CombN.ar(SinOsc.ar(f, 0, 0.04), 0.2, 0.2, 4);

zout * amp

});


// the patch specifies the actual values to be used

p = Patch(\bubbles, 

[ 

0.01 // speed is a fixed float of 0.01

// but amp is not specified, so the default 0.4 from the Instr function is used

]);


// but notice that it is a KrNumberEditor that was created, and it inits to the default 0.4

// from the instr function

p.gui;


)


If you wanted to build your patch with a KrNumberEditor (a slider) defaulted to 0.01 for the speed:


(

Patch(\bubbles,

[

KrNumberEditor(0.01,\speed)

]).gui

)


Automatic Input Creation


For any nil arguments, a default control will be created.  


This gives the impression that Patch is "an automatic gui for an Instr / SynthDef".  


If you do not supply arguments, it will make default ones, simple ones, but the real power of Patch is to supply functions with complex and varied inputs.  Sitting there with 5 sliders on a 1 dimensional Instrument isn't really the height of synthesis.


I recommend experimenting with factory methods to create your patches, supplying them with inputs useful for what you are working on.


eg. If you use a certain physical controller or wacom :


buildPatch  = { arg instrName;

var i;

i = Instr.at(instrName);

Patch(instrName,[

  { i.specAt(0).map( JoyAxis.kr(0,1,axis:5) ) },

  { i.specAt(1).map( JoyAxis.kr(0,1,axis:5) ) },

])

};

// this creates a Patch

buildPatch.value( \boingboing );


You could even interrogate the instr to see which inputs might make good candidates for your JoyAxis.


Remember, Instr are not just for audio functions, so you can even keep your factories themselves in Instrument libraries:


Instr(\joysticker,[ arg instrName;

var i;

i = Instr.at(instrName);

Patch(instrName,[

  { i.specAt(0).map( JoyAxis.kr(0,1,axis:5) ) },

  { i.specAt(1).map( JoyAxis.kr(0,1,axis:5) ) },

])

});



patch = Instr(\joysticker).value( \simple );


You have just used an Instr function to create and return a Patch.

This Instr is not used for audio, its just used to build and return a Patch


You could choose different controllers for different common inputs,

you can query the argument name and the spec.

Keep files in databases,  load other Patches or soundfiles from disk.

You could flip coins and choose from soundfiles, audio in, other saved 

patches or randomly chosen net radio feeds.



Fixed Arguments


Floats and other scalar values including Envelopes, are transparently dealt with by Patch.  These items are passed to the Instr function, but not to the SynthDef or the Synth.  They are not modulateable.


(

// fixing arguments


Instr([\jmcExamples,'moto-rev'],{ arg lfo=0.2,freq=1000,rq=0.1;

RLPF.ar(LFPulse.ar(SinOsc.kr(lfo, 0, 10, 21), [0,0.1], 0.1), freq, rq).clip2(0.4);

});


q = Patch([\jmcExamples,'moto-rev'],[

0.2

]);


q.gui;


)



You can design Instr to take parameters that are used only in the building of the SynthDef. This can be used to select from different kinds of filters or to .



Instr(\upOrDown, {arg upDown=0;

var line;

if (upDown>0,

{line = Line.kr(1,0,5)}, // upDown>0 ==> pitch goes up

{line = Line.kr(0,1,5)}  // upDown 0 or less ==> pitch goes down

);

SinOsc.ar(440*line,0,0.2);

},[

StaticIntegerSpec(0,1)

]);


Patch(\upOrDown, [ 0]).play


The upDown param acts as a switch between different synth def architectures.  If your Instr library is well designed you can acheive very sophisticated sound structures with automatic optimizations and code reuse.


Note that the Patch would assume upDown to be a modulatable control input (with a default of 0.0) without the StaticIntegerSpec making it clear that its a static integer.



Busses

(


s.boot;


a = Group.new;


b = Group.after(a);


c = Bus.audio(s,1);


p=Patch({ arg in,ffreq;

// the Bus is passed in as In.ar(bus.index,bus.numChannels)

LPF.ar(in,ffreq)

},[

c,

KrNumberEditor(3000,[200,8000,\exp])

]).play(group: b);


// play something onto this bus in a group before that of the filter

y = Patch({ Saw.ar(400) * 0.1  }).play(group: a, bus: c );



z = Patch({ Saw.ar(500) * 0.1  }).play(group: a, bus: c );

z.stop;


y.stop;


)


// you can make the bus play to a main audio output

c.play


//command-. to stop all



(

s.boot;


a = Group.new;


b = Group.after(a);


// no index, not yet allocated

c = Bus(\audio,nil,2);


y = Patch({ arg in,ffreq;

LPF.ar(in,ffreq)

},[

c, // a proxy, the bus is yet to be allocated

KrNumberEditor(3000,[200,8000,\exp])

]).play(group: b);


// now that the patch has played, the bus allocated itself

c.insp


// play onto this bus in a group before that of the filter

z = Patch({ Saw.ar([400,401]) * 0.1  }).play(group: a, bus: c )



Sending controllers to the Patch while its playing

Remapping the Bus while its playing



p = Patch({ arg freq=440; SinOsc.ar( freq ) });

// with no args supplied, a KrNumberEditor was made as input for \freq

p.args.dump;

p.play


// looks for the input for the 'freq' arg

// and sends it the .set(500) message if it responds to that

p.set(\freq, 500)


// change the bus

p.bus = 3


// change it back

p.bus = 0





Quite apart from Patch, you can use a spec to map a signal from a 0..1 range to the range of the spec :


(

var spec;

spec = [ 100,18000,\exp].asSpec;


{

SinOsc.ar(

// creates a BinaryOpUGen

  spec.map(  SinOsc.kr(0.1).range(0,1) ).dump

)

}.play


)



you can also do that with a Patch, supplying the resulting function as an input to the patch:


(

var spec;

spec = [ 100,18000,\exp].asSpec;


Patch({ arg freq;

SinOsc.ar(freq)

},[

// creates a BinaryOpFunction

    spec.map( { SinOsc.kr(0.1).range(0,1) } ).dump

]).play

)



A Patch is subclass of AbstractPlayer which is a subclass of AbstractFunction.


Because a Player IS A FUNCTION, a Spec may also be used to map another player's output and then use that as an input to a patch :


(

var spec;

spec = [ 100,18000,\exp].asSpec;


Patch({ arg freq;

SinOsc.ar(freq)

},[

// a BinaryOpFunction

    spec.map( Patch({ SinOsc.kr(0.1).range(0,1) }) ).debug

]).play


)




spec.map is taking the player

and creating a BinaryOpFunction out of it.


if you do math on functions you get another function.


f = { 3 } * { 4 };


f.value



the simplest example is:


(Patch({ SinOsc.ar(440) }) * 0.1).play


where the output of the Patch is multiplied by 0.1, reducing the amplitude.

Internal optimization results in using a PlayerAmp


// not yet implemented :(

(Patch({SinOsc.ar(440) }) % 0.4).play





Spawning


(


a = Patch({

SinOsc.ar(800,0.0)

});



c = Bus.audio;

a.play(bus: c);

// a is now playing on bus c, which we can't hear


// patch b will listen to buss c and play one enveloped grain

b = Patch({ arg tone;

var gate;

gate = Trig1.kr(1.0,0.25);

tone = In.ar(tone,1);

tone * EnvGen.kr(Env([0,1,0],[0.05,0.05],\welch,1),gate,doneAction: 2)

},[

c.index

]);


b.prepareForPlay(s);


// play one grain

b.spawn(atTime: 0.1);


// play 100 grains

Routine({

1.0.wait;

100.do({

b.spawn(atTime: 0.1);

0.25.wait

})

}).play(SystemClock)


)


see also [InstrGateSpawner]